#ifndef  M_SPECULAR_BXDF
#define M_SPECULAR_BXDF
#include"BxDF.h"
class SpecularReflection :public BxDF
{
private:
public:
	SpecularReflection() :BxDF(BxDFType(BSDF_SPECULAR | BSDF_REFLECTION)) {}
	virtual Spectrum f(P3 &light, P3 &view)
	{
		P3 refl(-light.x, -light.y, light.z);
		if (abs(refl.dot(view) - 1.0) < 1e-3) return 1.f/abs(view.z);
		return 0;
		//return 1.0;
	}
	virtual P3 ImportanceSample(P3 &light, Float* rands, Float *pdf)
	{
		*pdf = 1;
		return P3(-light.x,-light.y,light.z);
	}
	virtual Float getPdf(P3& light, P3& view) { 
		P3 refl(-light.x, -light.y, light.z);
		if (abs(refl.dot(view) - 1.0) < 1e-3) return 1;
		return 0;
	}
};

class SpecularTransmission :public BxDF
{
private:
	const Float eta;
public:
	SpecularTransmission(Float eta) :eta(eta), BxDF(BxDFType(BSDF_SPECULAR | BSDF_TRANSMISSION)) {}
	virtual Spectrum f(P3 &light, P3 &view)
	{
		bool entering = light.z > 0;
		Float etaI = entering ? 1 : eta;
		Float etaT = entering ? eta : 1;
		Float _eta = etaI / etaT;
		P3 norm(0, 0, 1);
		if (!entering) norm.z = -1;
		Float cosThetaI = norm.dot(light);
		Float sin2ThetaI = fmax(0, 1.f - cosThetaI * cosThetaI);
		Float sin2ThetaT = _eta * _eta * sin2ThetaI;
		// total internal reflection
		if (sin2ThetaT >= 1) {
			return 0.;
		}
		Float cosThetaT = sqrtf(1 - sin2ThetaT);
		P3 refr_dir = -light * _eta + norm * (_eta * cosThetaI - cosThetaT);
		if (abs(view.dot(refr_dir) - 1.0f) < 1e-3)
			return _eta * _eta / abs(view.z);
		return 0;
	}
	virtual P3 ImportanceSample(P3 &light, Float* rands, Float *pdf)
	{
		/*
		P3 norm(0, 0, 1);
		norm.z = light.z < 0 ? -1 : 1;
		P3 refr_dir;
		if (light.z < 0)
			refr_dir = (light).refract(norm, eta, 1).norm();
		else
			refr_dir = (light).refract(norm, 1, eta).norm();

		*pdf = refr_dir.len2() > 0 ? 1 : 0;
		//*pdf = 1;
		return refr_dir;*/
		bool entering = light.z > 0;
		Float etaI = entering ? 1 : eta;
		Float etaT = entering ? eta : 1;
		Float _eta = etaI / etaT;
		P3 norm(0, 0, 1);
		if (!entering) norm.z = -1;
		Float cosThetaI = norm.dot(light);
		Float sin2ThetaI = fmax(0, 1.f - cosThetaI * cosThetaI);
		Float sin2ThetaT = _eta * _eta * sin2ThetaI;
		// total internal reflection
		if (sin2ThetaT >= 1) {
			*pdf = 0;
			return 0.;
		}
		*pdf = 1;
		Float cosThetaT = sqrtf(1 - sin2ThetaT);
		volatile P3 refr_dir = -light * _eta + norm * (_eta * cosThetaI - cosThetaT);
		return P3(refr_dir.x,refr_dir.y,refr_dir.z);
	}
	virtual Float getPdf(P3& light, P3& view) { 
		bool entering = light.z > 0;
		Float etaI = entering ? 1 : eta;
		Float etaT = entering ? eta : 1;
		Float _eta = etaI / etaT;
		P3 norm(0, 0, 1);
		if (!entering) norm.z = -1;
		Float cosThetaI = norm.dot(light);
		Float sin2ThetaI = fmax(0, 1.f - cosThetaI * cosThetaI);
		Float sin2ThetaT = _eta * _eta * sin2ThetaI;
		// total internal reflection
		if (sin2ThetaT >= 1) {
			return 0.;
		}
		Float cosThetaT = sqrtf(1 - sin2ThetaT);
		P3 refr_dir = -light * _eta + norm * (_eta * cosThetaI - cosThetaT);
		if (abs(view.dot(refr_dir) - 1.0f) < 1e-3)
			return 1;
		return 0;
	}
};

#endif // ! M_SPECULAR_BXDF

